package android_serialport_api.hyperlcd;

import static android_serialport_api.hyperlcd.TransformUtils.byte2AsciiString;
import static android_serialport_api.hyperlcd.TransformUtils.bytes2HexString;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

/**
 * Created by ADan on 2017/9/16.
 */

class SerialPort {

    private android_serialport_api.SerialPort serialPort;
    private InputStream inputStream;
    private OutputStream outputStream;
    private ReadThread readThread;
    private String port;
    private boolean open;
    private boolean isAscii;
    private int baudRate;
    private int flags;
    private LogInterceptorSerialPort logInterceptor;

    public SerialPort(String port, boolean isAscii, int baudRate, int flags) {
        this.port = port;
        this.isAscii = isAscii;
        this.baudRate = baudRate;
        this.flags = flags;
    }

    public void setLogInterceptor(LogInterceptorSerialPort logInterceptor) {
        this.logInterceptor = logInterceptor;
    }

    private void log(@SerialPortManager.Type String type, String port, boolean isAscii, CharSequence log) {
        log(type, port, isAscii, log == null ? "null" : log.toString());
    }

    private void log(@SerialPortManager.Type String type, String port, boolean isAscii, String log) {
        if (logInterceptor != null) {
            logInterceptor.log(type, port, isAscii, log);
        }
    }

    /**
     * 启动串口 默认波特率9600
     *
     * @param reader
     */
    public boolean open(BaseReader reader) {
        log(SerialPortManager.port, port, isAscii,
                new StringBuffer().append("波特率：")
                        .append(baudRate).append(" 标记位：").append(flags)
                        .append(" 启动串口"));
        if (open) {
            log(SerialPortManager.port, port, isAscii, new StringBuffer().append("启动失败：串口已启动"));
            return open;
        }
        try {
            serialPort = new android_serialport_api.SerialPortV2(new File(port), baudRate, flags);
            if (serialPort == null) {
                log(SerialPortManager.port, port, isAscii, new StringBuffer().append("启动失败：SerialPort == null"));
            } else {
                inputStream = serialPort.getInputStream();
                if (inputStream == null) {
                    throw new Exception("inputStream==null");
                }
                outputStream = serialPort.getOutputStream();
                if (outputStream == null) {
                    throw new Exception("outputStream==null");
                }
                readThread = new ReadThread(isAscii, reader);
                readThread.start();
                open = true;
                log(SerialPortManager.port, port, isAscii, new StringBuffer().append("启动成功"));
            }
        } catch (Exception e) {
            e.printStackTrace();
            log(SerialPortManager.port, port, isAscii, new StringBuffer().append("启动失败：").append(e));
            open = false;
        }
        return open;
    }

    public boolean isOpen() {
        return open;
    }

    public void setReadCode(boolean isAscii) {
        if (readThread != null) {
            readThread.isAscii = isAscii;
            log(SerialPortManager.port, port, readThread.isAscii, new StringBuffer().append("修改数据格式：").append(isAscii ? "ASCII" : "HexString"));
        }
    }

    public void setReader(BaseReader reader) {
        if (readThread != null) {
            readThread.setReader(reader);
        }
    }

    class ReadThread extends Thread {

        public boolean isRun;
        public boolean isAscii;
        private BaseReader reader;

        public ReadThread(boolean isAscii, BaseReader baseReader) {
            reader = baseReader;
            this.isAscii = isAscii;
            if (reader != null) {
                reader.setLogInterceptor(logInterceptor);
            }
        }

        @Override
        public void run() {
            if (inputStream == null) {
                return;
            }
            isRun = true;
            while (isRun && !isInterrupted()) {
                try {
                    // 防止线程IO 阻塞
                    if (inputStream.available() > 0) {

                        int size;
                        byte[] buffer = new byte[2048];
                        /**
                         * 读不到数据时该方法会一直等待，直到读到数据
                         * 处于io阻塞状态时该线程不可中断，即使中断，有数据过来时，程序也会执行到中断标记处，所以
                         */
                        size = inputStream.read(buffer);
//                        while (inputStream.available() > 0) {
//                            int nSzie = inputStream.read(buffer, size, inputStream.available());
//                            size += nSzie;
//                        }
//                        while (size < 8) {
//                            Thread.sleep(100);
//                            int av = inputStream.available();
//                            if (size + av < 8) {
//                                size = 0;
//                                break;
//                            } else {
//                                av = inputStream.read(buffer, size, inputStream.available());
//                                size += av;
//                            }
//                        }

                        if (!isRun) {
                            break;
                        }
                        if (reader != null) {
                            if (size > 0) {
                                reader.onBaseRead(port, isAscii, buffer, size);
                            }
                        }
                    }
//                    Thread.sleep(50);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            log(SerialPortManager.port, port, isAscii, "线程终止 释放资源成功");
        }

        public void stopRead() {
            isRun = false;
        }

        public void setReader(BaseReader baseReader) {
            reader = baseReader;
            if (reader != null) {
                reader.setLogInterceptor(logInterceptor);
            }
        }
    }

    public void write(String cmd) {
        write(isAscii, cmd);
    }
    public void write(boolean isAscii, String cmd) {
        byte[] bytes;
        if (isAscii) {
            bytes = cmd.getBytes();
        } else {
            bytes = TransformUtils.hexStringToBytes(cmd);
        }
        write(bytes);
    }

    public void write( byte[] cmd) {
        String str = null;
        if (isAscii) {
            str = byte2AsciiString(cmd, cmd.length);
        } else {
             str = bytes2HexString(cmd, cmd.length);

        }
        log(SerialPortManager.write, port, isAscii, new StringBuffer().append("写：").append(str));
        if (outputStream != null) {
            synchronized (outputStream) {
                try {
                    outputStream.write(cmd);
                } catch (Exception e) {
                    log(SerialPortManager.write, port, isAscii, new StringBuffer().append("写失败:").append(e));
                }
            }
//            log(SerialPortManager.write, port, isAscii, new StringBuffer().append("写成功：").append(bytes2HexString(cmd, cmd.length)));
        } else {
            log(SerialPortManager.write, port, isAscii, new StringBuffer().append("写失败：outputStream is null"));
        }
    }

    public void close() {
        try {
            open = false;
            if (readThread != null) {
                readThread.stopRead();
                log(SerialPortManager.port, port, readThread.isAscii, "关闭串口成功");
            } else {
                log(SerialPortManager.port, port, false, "关闭串口失败：串口未开启");
            }

            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (serialPort != null) {
                serialPort.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
